package com.weishi.project.training.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weishi.common.utils.DateUtils;
import com.weishi.common.utils.StringUtils;
import com.weishi.project.speedometer.domain.EmaFilter;
import com.weishi.project.speedometer.vo.PropellerFrequencyData;
import com.weishi.project.training.domain.AccelerationLog;
import com.weishi.project.training.mapper.AccelerationLogMapper;
import com.weishi.project.training.service.IAccelerationLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

@Service
public class AccelerationLogServiceImpl extends ServiceImpl<AccelerationLogMapper, AccelerationLog> implements IAccelerationLogService {

    @Autowired
    Environment environment;

    private static double g = 9.80665;//重力加速度

    @Autowired
    private AccelerationLogMapper accelerationLogMapper;

    Logger logger = Logger.getLogger("AccelerationLogServiceImpl");

    List<AccelerationLog> selectAccelerationLogList(AccelerationLog accelerationLog) {
        return accelerationLogMapper.selectAccelerationLogList(accelerationLog);
    }

    @Override
    public int qryCurrentPropellerFrequency(String devId, Integer channelId) {
        AccelerationLog params = new AccelerationLog();
        params.setDevId(devId);
        params.setChannelId(channelId);
        List<AccelerationLog> accelerationLogs = accelerationLogMapper.selectAccelerationLogList(params);
        return qryCurrentPropellerFrequency(accelerationLogs);
    }

    @Override
    public int qryCurrentPropellerFrequency(List<AccelerationLog> accelerationLogs) {
        int count = 4;
        int propellerFrequency = 0;
        List<PropellerFrequencyData> list = getPropellerFrequencyData(accelerationLogs, count, 0);
        if (list != null && list.size() > 1) {
            PropellerFrequencyData data = list.get(list.size() - 2);
            propellerFrequency = (int) data.getAveragePropeller();

            /*if (list.size() > count) {
                Double propellerFrequencyTotal = 0.0;
                for(int i = list.size()-2; i>=list.size() - count; i--){
                    propellerFrequencyTotal += (int) list.get(i).getAveragePropeller();
                }
                propellerFrequency = (int)(propellerFrequencyTotal/count-1);
            }*/
        } else if (list != null && list.size() == 1) {
            PropellerFrequencyData data = list.get(list.size() - 1);
            propellerFrequency = (int) data.getAveragePropeller();
        }
        return propellerFrequency;
    }

    @Override
    public List<PropellerFrequencyData> qryPropellerFrequencyDate(String devId, Integer channelId, long fristTime) {
        AccelerationLog params = new AccelerationLog();
        if(StringUtils.isNotEmpty(devId))params.setDevId(devId);
        params.setChannelId(channelId);
        List<AccelerationLog> accelerationLogs = accelerationLogMapper.selectAccelerationLogList(params);
        return getPropellerFrequencyData(accelerationLogs, 4, fristTime);
    }

    /**
     * @param dataVos
     * @param count     一次桨频算一次平均桨频
     * @param fristTime 开始时间
     * @return
     */
    private List<PropellerFrequencyData> getPropellerFrequencyData(List<AccelerationLog> dataVos, int count, long fristTime) {
        List<AccelerationLog> list = getImportantPoint(dataVos);
        List<PropellerFrequencyData> ret = new ArrayList<>();
        List<Double> averagePropellers = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            PropellerFrequencyData data = new PropellerFrequencyData();
            if (list.get(i).getCount() == -1) {

                data.setStartTime(list.get(i).getTimp());
                data.setRealTime(DateUtils.getStringTime(Long.parseLong(list.get(i).getTimp()) - fristTime));
                if ((i + count) < list.size()) {

                    //判断相邻点的间隙时间是否约等于，确定实际计数点
                    List<Long> ts = new ArrayList<>();
                    Long pre = Long.parseLong(list.get(i).getTimp());
                    for(int j=1;j<=count;j++){
                        Long te = Long.parseLong(list.get(i+j).getTimp());
                        ts.add(te-pre);
                        pre = te;
                    }

                    long countTemp = getActualCount(ts);
//                    long countTemp = count;

                    data.setEndTime(list.get(i + count).getTimp());
                    double minute = (Long.parseLong(data.getEndTime()) - Long.parseLong(data.getStartTime())) / 1000d;
                    double averagePropeller = countTemp / (minute / 60.0);
                    data.setAveragePropeller(averagePropeller);
                    data.setCount(count);
                } else {
                    AccelerationLog temp = list.get(i);
                    long count1 = count;
                    //总共桨数小于计次桨频数
                    if(count >= list.size()){
                        temp = list.get(list.size() - 1);
                        count1 = list.size()-i;
                    }else{
                        //最后几桨和前面的数据取平局值
                        if(i - count > 0 ){
                            data.setStartTime(list.get(i-count).getTimp());
                            //判断相邻点的间隙时间是否约等于，确定实际计数点
                            List<Long> ts = new ArrayList<>();
                            Long pre = Long.parseLong(list.get(i-count).getTimp());
                            for(int j=count-1;j>=0;j--){
                                Long te = Long.parseLong(list.get(i-j).getTimp());
                                ts.add(te-pre);
                                pre = te;
                            }
                            count1 = getActualCount(ts);
                        }else{
                            data.setStartTime(list.get(0).getTimp());
                            count1 = i;
                        }
                    }
                    data.setEndTime(temp.getTimp());
                    double minute = (Long.parseLong(data.getEndTime()) - Long.parseLong(data.getStartTime())) / 60000d;

                    double averagePropeller = minute != 0 ? count1 / minute : 0.0;
                    /*if (averagePropeller > 160.0) {
                        averagePropeller = i / ((Long.parseLong(list.get(i).getTimp()) - fristTime) / 60000d);
                        if (averagePropeller > 200.0) {
                            averagePropeller = 190;
                        }
                    }*/
                    data.setAveragePropeller(averagePropeller);
                    data.setCount((int)count1);
                }

                ret.add(data);
                averagePropellers.add(data.getAveragePropeller());
            }
        }

        //卡尔曼算法平滑
        /*KalmanFilter kalmanFilter = new KalmanFilter();
        averagePropellers = kalmanFilter.kalman(averagePropellers);*/

        //平均数平滑
        averagePropellers = smooth(averagePropellers);


        //数据线性化
        for (int i = 0; i < ret.size(); i++) {
            PropellerFrequencyData p = ret.get(i);
            p.setAveragePropeller(averagePropellers.get(i));
        }
        return ret;
    }

    @Override
    public Object qryAccelerationLogList(Integer channelId) {
       /* AccelerationLog params = new AccelerationLog();
        params.setChannelId(channelId);
        return getImportantPoint(selectAccelerationLogList(params));*/
        return null;
    }

    /**
     * 获取波峰
     *
     * @param dataVos
     * @return
     */
    @Override
    public List<AccelerationLog> getImportantPoint(List<AccelerationLog> dataVos) {
        List<AccelerationLog> retList = new ArrayList<>();
        List<AccelerationLog> tempList = new ArrayList<>();

        String type = environment.getProperty("system.version");

        List<Double> distances = new ArrayList<>();
        for (AccelerationLog accelerationLog : dataVos) {
//            double z = getZ(accelerationLog.getXcode(),accelerationLog.getYcode(),accelerationLog.getZcode());
//            double z = Math.sqrt(accelerationLog.getXcode()*accelerationLog.getXcode()+accelerationLog.getYcode()*accelerationLog.getYcode()+accelerationLog.getZcode()*accelerationLog.getZcode());
            double z = accelerationLog.getZcode();
            //去除无效的点
            if ((z <= 2.0 && z >= -1.0)) {
                continue;
            }
            distances.add(z);
            tempList.add(accelerationLog);
        }

        if(distances==null || distances.size()==0){
            return retList;
        }

        //卡尔曼算法
       /* KalmanFilter kalmanFilter = new KalmanFilter();
        distances = kalmanFilter.kalman(distances);*/

        //指数滤波(EMA)
        /*List<Double> medians = new ArrayList<>();
        distances = EmaFilter.ema(distances,medians,0);*/
        distances = EmaFilter.ema(distances, 0);
        //第二次滤波
//        distances = EmaFilter.ema(distances, 0);
        //第三次滤波
//        distances = EmaFilter.ema(distances, 0);

        try {
            double[] data = new double[tempList.size()];
            int index = 0;
            for (Double aa : distances) {
                data[index++] = aa;
            }
            int[] PeakAndTrough = new int[data.length];

            //需要三个不同的值进行比较，取lo,mid，hi分别为三值
            for (int lo = 0, mid = 1, hi = 2; hi < data.length; hi++) {
                //先令data[lo]不等于data[mid]
                while (mid < data.length && data[mid] == data[lo]) {
                    mid++;
                }

                hi = mid + 1;

                //令data[hi]不等于data[mid]
                while (hi < data.length && data[hi] == data[mid]) {
                    hi++;
                }

                if (hi >= data.length) {
                    break;
                }

                //检测是否为峰值
                if (data[mid] > data[lo] && data[mid] > data[hi]) {
                    PeakAndTrough[mid] = 1;       //1代表波峰
                } else if (data[mid] < data[lo] && data[mid] < data[hi]) {
                    PeakAndTrough[mid] = -1;      //-1代表波谷
                }

                lo = mid;
                mid = hi;
            }

            //计算均值
            float ave = 0;
            for (int i = 0; i < data.length; i++) {
                ave += data[i];
            }
            ave /= data.length;

            //排除大于均值的波谷和小于均值的波峰
            for (int i = 0; i < PeakAndTrough.length; i++) {
                if ((PeakAndTrough[i] > 0 && data[i] < ave) || (PeakAndTrough[i] < 0 && data[i] > ave)) {
                    PeakAndTrough[i] = 0;
                }
            }

            //统计波峰数量（去除小于0的波峰，大于0的波谷）
            for (int i = 0; i < PeakAndTrough.length; i++) {
//                double avgTemp = (ave + data[i]) / 2.0;
                double avgTemp = 0;
                if (PeakAndTrough[i] == -1 && data[i] < avgTemp) {
                    AccelerationLog log = tempList.get(i);
                    log.setCount(PeakAndTrough[i]);
                    log.setYcode(data[i]);
                    log.setCount(-1);
                    retList.add(log);
                }
                if (PeakAndTrough[i] == 1 && data[i] > avgTemp) {
                    AccelerationLog log = tempList.get(i);
                    log.setCount(PeakAndTrough[i]);
                    log.setYcode(data[i]);
                    log.setCount(1);
                    retList.add(log);
                }
            }

            tempList = optimization(retList);
            retList.clear();
            for (AccelerationLog tt : tempList) {
                if (tt.getCount() == -1) {
                    retList.add(tt);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return retList;
    }

    private List<AccelerationLog> optimization(List<AccelerationLog> logs) {

        List<AccelerationLog> retList = new ArrayList<>();

        if (StringUtils.isEmpty(logs)) {
            return retList;
        }
        int flag = logs.get(0).getCount();
        AccelerationLog pre = new AccelerationLog();//上个有效点标记

        List<AccelerationLog> temp = new ArrayList<>();//多个连续重复的点，用于统计合适的点
        //有波峰必有波谷
        for (AccelerationLog accelerationLog : logs) {
            if (accelerationLog.getCount() != flag) {
                if (temp.size() > 1) {
                    AccelerationLog log = getOptimal(temp, pre);
                    retList.add(log);
                    pre = log;
                } else if (temp.size() == 1) {
                    retList.add(temp.get(0));
                    pre = temp.get(0);
                }
                temp.clear();
            }

            flag = accelerationLog.getCount();
            temp.add(accelerationLog);
        }
        accelerationLogFormat(retList);
        return retList;
    }

    /**
     * 根据时间间隔纠错
     *
     * @param accelerationLogs
     */
    private void accelerationLogFormat(List<AccelerationLog> accelerationLogs) {
        //时间间隙 根据时间间隙判断误差值
        long timpClearance = 0;
        AccelerationLog pre = null;
        List<Long> clearances = new ArrayList<>();
        for (AccelerationLog accelerationLog : accelerationLogs) {
            if (accelerationLog.getCount() == -1 && pre != null) {
                timpClearance = Long.parseLong(accelerationLog.getTimp()) - Long.parseLong(pre.getTimp());
                clearances.add(timpClearance);
            }
            pre = accelerationLog;

        }
    }

    /**
     * 获取最优的点
     *
     * @param temp
     * @param pre  参考点
     * @return
     */
    private AccelerationLog getOptimal(List<AccelerationLog> temp, AccelerationLog pre) {
        AccelerationLog accelerationLog = temp.get(0);
        for (AccelerationLog log : temp) {
            double dd = log.getCount() * log.getZcode();
            if (dd > log.getCount() * accelerationLog.getZcode()) {
                accelerationLog = log;
            }
        }
        return accelerationLog;
    }

    /**
     * 数组平滑处理
     *
     * @param d
     * @return
     */
    public static List<Double> smooth(List<Double> d) {
        int length = d.size();
        Double[] dbRt = new Double[length];
        if (length == 1) {
            /** 前置与后置无元素 index=0与index=length-1 */
            dbRt[0] = d.get(0);
            dbRt[length - 1] = d.get(length - 1);
        }
        if (length == 2) {
            /** 前置与后置无元素 index=0与index=length-1 */
            dbRt[0] = d.get(0);
            dbRt[length - 1] = d.get(length - 1);
        }
        if (length == 3) {
            /** 前置与后置无元素 index=0与index=length-1 */
            dbRt[0] = d.get(0);
            dbRt[length - 1] = d.get(length - 1);
            /** 前置与后置只有一个元素 index=1 与index=length-2 */
            dbRt[1] = (d.get(0) + d.get(1) + d.get(2)) / 3;
        }
        if (length == 4) {
            /** 前置与后置无元素 index=0与index=length-1 */
            dbRt[0] = d.get(0);
            dbRt[length - 1] = d.get(length - 1);
            /** 前置与后置只有一个元素 index=1 与index=length-2 */
            dbRt[1] = (d.get(0) + d.get(1) + d.get(2)) / 3;
            dbRt[2] = (d.get(1) + d.get(2) + d.get(3)) / 3;
        }

        if (length >= 5) {
            /** 前置与后置无元素 index=0与index=length-1 */
            dbRt[0] = d.get(0);
            /** 前置与后置只有一个元素 index=1 与index=length-2 */
            dbRt[1] = (d.get(0) + d.get(1) + d.get(2)) / 3;
            for (int x = 2; x < length - 2; x++) {
                /** 前置与后置均由两个元素情况 d.get(n] n-2>=0且n+2<=length-1 */
                dbRt[x] = (d.get(x - 2) + d.get(x - 1) + d.get(x) + d.get(x + 1) + d.get(x + 2)) / 5;
            }

            dbRt[length - 2] = (d.get(length - 3) + d.get(length - 2) + d.get(length - 1)) / 3;
            dbRt[length - 1] = d.get(length - 1);
        }
        List<Double> ret = Arrays.asList(dbRt);
        return ret;
    }

    /**
     * 去除重力加速度在x轴上的影响
     *
     * @param x
     * @param y
     * @param z
     * @return
     */
    public static double getX(double x, double y, double z) {
        double d1 = Math.atan(Math.sqrt(y * y + z * z) / x);
        double gX = Math.cos(d1) * g;
        x = x >= 0 ? x - gX : x + gX;
        double f1 = new BigDecimal(x).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
        return f1;
    }

    public static double getY(double x, double y, double z) {
        double d1 = Math.atan(Math.sqrt(x * x + z * z) / y);
        double gY = Math.cos(d1) * g;
        y = y >= 0 ? y - gY : y + gY;
        double f1 = new BigDecimal(y).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
        return f1;
    }

    public static double getZ(double x, double y, double z) {
        double d1 = Math.atan(Math.sqrt(y * y + x * x) / z);
        double gZ = Math.cos(d1) * g;
        z = gZ;
        double f1 = new BigDecimal(z).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
        return f1;
    }

    /**
     * 根据时间间隔，算出真实次数
     * @return
     */
    private long getActualCount(List<Long> ts){
        String type = environment.getProperty("system.version");

        //皮艇桨频区间 460ms-670ms(90-130桨)
        //划艇桨频区间 850ms-1500ms(40-70桨)
        //赛艇桨频区间 1200ms-3000ms(20-50桨)
        int t1 = 1200;int t2 = 3000;
        if(type.equals("p")){
            t1 = 460;t2 =670;
        }else if(type.equals("h")){
            t1 = 850;t2 = 1500;
        }
        long count = ts.size();
        double avg = 0;
        for(Long t : ts){
            if(t1<t && t2>t && (avg-t<100 || t-avg<100)){
                avg = (t + avg)/2;
            }
        }

        if(avg==0){
            return count;
        }

        for(long t : ts){
            long countTemp = 1;
            if(t >= avg){
                countTemp = Math.round(t/avg);
                if(countTemp==1 || countTemp==0){
                    continue;
                }else{
                    countTemp = countTemp>2?1:countTemp;
                    count = count + (countTemp-1);
                }
            }else{
                countTemp = Math.round(avg/t);
                if(countTemp==1 || countTemp==0){
                    continue;
                }else{
                    countTemp = countTemp>2?1:countTemp;
                    count = count - countTemp;
                }
            }
        }
        return count>0?count:ts.size();
    }

}
